// -----------------------------------------------------------------------
//  <copyright file="Extensions.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2024 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://www.dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2024-04-18 16:04</last-date>
// -----------------------------------------------------------------------

using System.Text.Json;

using DaprPlus.Properties;


namespace DaprPlus.Filters;

public static class Extensions
{
    #region 字段

    private static readonly Dictionary<FilterOperate, Func<Expression, Expression, Expression>> ExpressionDict =
        new()
        {
            {
                FilterOperate.Equal, Expression.Equal
            },
            {
                FilterOperate.NotEqual, Expression.NotEqual
            },
            {
                FilterOperate.Less, Expression.LessThan
            },
            {
                FilterOperate.Greater, Expression.GreaterThan
            },
            {
                FilterOperate.LessOrEqual, Expression.LessThanOrEqual
            },
            {
                FilterOperate.GreaterOrEqual, Expression.GreaterThanOrEqual
            },
            {
                FilterOperate.StartsWith,
                (left, right) =>
                {
                    if (left.Type != typeof(string))
                    {
                        throw new NotSupportedException("\"StartsWith\"比较方式只支持字符串类型的数据");
                    }
                    return Expression.Call(left,
                        typeof(string).GetMethod("StartsWith", [typeof(string)])
                        ?? throw new InvalidOperationException("名称为\"StartsWith\"的方法不存在"),
                        right);
                }
            },
            {
                FilterOperate.EndsWith,
                (left, right) =>
                {
                    if (left.Type != typeof(string))
                    {
                        throw new NotSupportedException("\"EndsWith\"比较方式只支持字符串类型的数据");
                    }
                    return Expression.Call(left,
                        typeof(string).GetMethod("EndsWith", [typeof(string)])
                        ?? throw new InvalidOperationException("名称为\"EndsWith\"的方法不存在"),
                        right);
                }
            },
            {
                FilterOperate.Contains,
                (left, right) =>
                {
                    if (left.Type != typeof(string))
                    {
                        throw new NotSupportedException("\"Contains\"比较方式只支持字符串类型的数据");
                    }
                    return Expression.Call(left,
                        typeof(string).GetMethod("Contains", [typeof(string)])
                        ?? throw new InvalidOperationException("名称为\"Contains\"的方法不存在"),
                        right);
                }
            },
            {
                FilterOperate.NotContains,
                (left, right) =>
                {
                    if (left.Type != typeof(string))
                    {
                        throw new NotSupportedException("\"NotContains\"比较方式只支持字符串类型的数据");
                    }
                    return Expression.Not(Expression.Call(left,
                        typeof(string).GetMethod("Contains", [typeof(string)])
                        ?? throw new InvalidOperationException("名称为\"Contains\"的方法不存在"),
                        right));
                }
            }
            //{
            //    FilterOperates.StdIn, (left, right) =>
            //    {
            //        if (!right.Type.IsArray)
            //        {
            //            return null;
            //        }
            //        return left.Type != typeof (string) ? null : Expression.Call(typeof (Enumerable), "Contains", new[] {left.Type}, right, left);
            //    }
            //},
            //{
            //    FilterOperates.DataTimeLessThanOrEqual, Expression.LessThanOrEqual
            //}
        };

    #endregion

    /// <summary>
    /// 将FilterGroup转换为查询表达式
    /// </summary>
    public static Expression<Func<T, bool>> ToExpression<T>(this FilterGroup group)
    {
        Check.NotNull(group, nameof(group));

        var param = Expression.Parameter(typeof(T), "m");
        var body = GetExpressionBody(param, group);
        var expression = Expression.Lambda<Func<T, bool>>(body, param);
        return expression;
    }

    private static Expression GetExpressionBody(ParameterExpression param, FilterGroup group)
    {
        param.CheckNotNull("param");

        //如果无条件或条件为空，直接返回 true表达式
        if (group.Rules.Count == 0 && group.Groups.Count == 0)
        {
            return Expression.Constant(true);
        }
        var bodies = new List<Expression>();
        bodies.AddRange(group.Rules.Select(rule => GetExpressionBody(param, rule)));
        bodies.AddRange(group.Groups.Select(subGroup => GetExpressionBody(param, subGroup)));

        return group.Operate switch
        {
            FilterOperate.And => bodies.Aggregate(Expression.AndAlso),
            FilterOperate.Or => bodies.Aggregate(Expression.OrElse),
            _ => throw new DaprPlusException(ResDaprPlus.String_Filter_OperateError)
        };
    }

    private static Expression GetExpressionBody(ParameterExpression param, FilterRule? rule)
    {
        if (rule == null)
        {
            return Expression.Constant(true);
        }
        var expression = GetPropertyLambdaExpression(param, rule);
        if (expression == null)
        {
            return Expression.Constant(true);
        }
        var constant = ChangeTypeToExpression(rule, expression.Body.Type);
        return ExpressionDict[rule.Operate](expression.Body, constant);
    }

    private static LambdaExpression? GetPropertyLambdaExpression(ParameterExpression param, FilterRule rule)
    {
        var propertyNames = rule.Field.Split('.');
        if (rule.IsLowerCaseToUpperCase)
        {
            propertyNames = propertyNames.Select(m => m.ToUpperCase()).ToArray();
        }
        Expression propertyAccess = param;
        var type = param.Type;
        for (var index = 0; index < propertyNames.Length; index++)
        {
            var propertyName = propertyNames[index];
            var property = type.GetProperty(propertyName);
            if (property == null)
            {
                throw new InvalidOperationException(ResDaprPlus.Format_Filter_Property_NotExist.FormatWith(rule.Field, type.FullName));
            }

            type = property.PropertyType;
            //验证最后一个属性与属性值是否匹配
            if (index == propertyNames.Length - 1)
            {
                var flag = CheckFilterRule(type, rule);
                if (!flag)
                {
                    return null;
                }
            }

            propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
        }
        return Expression.Lambda(propertyAccess, param);
    }

    /// <summary>
    /// 验证最后一个属性与属性值是否匹配 
    /// </summary>
    /// <param name="type">最后一个属性</param>
    /// <param name="rule">条件信息</param>
    /// <returns></returns>
    private static bool CheckFilterRule(Type type, FilterRule rule)
    {
        if (rule.Value == null || rule.Value.ToString() == string.Empty)
        {
            rule.Value = null;
        }

        if (rule.Value == null && (type == typeof(string) || type.IsNullableType()))
        {
            return rule.Operate is FilterOperate.Equal or FilterOperate.NotEqual;
        }

        if (rule.Value == null)
        {
            return !type.IsValueType;
        }
        return true;
    }

    private static Expression ChangeTypeToExpression(FilterRule rule, Type conversionType)
    {
        //if (item.Method == QueryMethod.StdIn)
        //{
        //    Array array = (item.Value as Array);
        //    List<Expression> expressionList = new List<Expression>();
        //    if (array != null)
        //    {
        //        expressionList.AddRange(array.Cast<object>().Select((t, i) =>
        //            ChangeType(array.GetValue(i), conversionType)).Select(newValue => Expression.Constant(newValue, conversionType)));
        //    }
        //    return Expression.NewArrayInit(conversionType, expressionList);
        //}

        // 处理当前用户信息动态值查询
        var strValue = rule.Value?.ToString();
        if (strValue == null)
        {
            return Expression.Constant(null, conversionType);
        }

        return HandleNormalValue(rule, conversionType);
    }

    private static Expression HandleNormalValue(FilterRule rule, Type conversionType)
    {
        object? value;
        if (rule.Value is JsonElement element)
        {
            value = element.ConvertToObject();
            value = value?.CastTo(conversionType);
        }
        else
        {
            value = rule.Value.CastTo(conversionType);
        }
        return Expression.Constant(value, conversionType);
    }
    private static bool IsNullableType(this Type type)
    {
        return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
    }

}
